import os
import time
import hashlib
import subprocess
import uuid
from flask import Flask, request, jsonify, render_template, url_for, send_from_directory
from werkzeug.utils import secure_filename
from moviepy.editor import (
    ImageClip, 
    AudioFileClip, 
    CompositeVideoClip, 
    TextClip, 
    concatenate_videoclips,
    VideoFileClip
)
import pysrt
import logging
from PIL import Image

# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# 配置
UPLOAD_FOLDER = 'uploads'
OUTPUT_FOLDER = 'outputs'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'mp3', 'srt'}

app = Flask(__name__)

# 配置
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['OUTPUT_FOLDER'] = OUTPUT_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 32 * 1024 * 1024  # 32MB限制

def allowed_file(filename):
    """检查文件扩展名是否允许"""
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def create_video_segment(subtitle_file, audio_file, image_file):
    """创建视频片段"""
    try:
        # 加载字幕
        app.logger.info(f"加载字幕文件: {subtitle_file}")
        subs = pysrt.open(subtitle_file)
        app.logger.info(f"成功加载字幕，共{len(subs)}条")
        
        # 加载音频
        app.logger.info(f"加载音频文件: {audio_file}")
        audio = AudioFileClip(audio_file)
        
        # 加载图片
        app.logger.info(f"加载图片文件: {image_file}")
        image = ImageClip(image_file).set_duration(audio.duration)
        
        # 设置fps
        image.fps = 24
        
        # 创建字幕剪辑
        subtitle_clips = []
        for i, sub in enumerate(subs):
            start_time = sub.start.ordinal / 1000  # 转换为秒
            end_time = sub.end.ordinal / 1000  # 转换为秒
            
            app.logger.info(f"处理字幕 #{i+1}: '{sub.text}', 开始时间: {start_time}秒, 结束时间: {end_time}秒")
            
            try:
                # 创建文本剪辑
                text_clip = TextClip(
                    txt=sub.text,
                    fontsize=72,   # 更大的字体
                    color='white',
                    font='AR-PL-UMing-CN',  # 使用更通用的字体
                    method='caption',
                    size=(image.w * 0.9, None),  # 宽度稍微收窄，以免太靠边
                    align='center',
                    stroke_color='gold',  # 添加黑色描边
                    stroke_width=2  # 描边宽度
                )
                
                # 设置位置、持续时间和开始时间
                # 计算字幕位置：距离底部50像素
                bottom_margin = 50
                text_clip = text_clip.set_position(('center', image.h - text_clip.h - bottom_margin))
                text_clip = text_clip.set_start(start_time)
                text_clip = text_clip.set_duration(end_time - start_time)
                
                subtitle_clips.append(text_clip)
                app.logger.info(f"字幕 #{i+1} 处理成功")
            except Exception as e:
                app.logger.error(f"处理字幕 #{i+1} 时出错: {str(e)}")
        
        app.logger.info(f"成功创建 {len(subtitle_clips)} 个字幕剪辑")
        
        # 合成视频
        app.logger.info("合成视频")
        clips_to_composite = [image] + subtitle_clips
        app.logger.info(f"合成 {len(clips_to_composite)} 个剪辑")
        video = CompositeVideoClip(clips_to_composite)
        
        # 添加音频
        app.logger.info("添加音频")
        video = video.set_audio(audio)
        
        return video
    
    except Exception as e:
        app.logger.error(f"创建视频片段时出错: {str(e)}")
        raise

@app.route('/')
def index():
    """渲染主页"""
    return render_template('index.html')

@app.route('/upload', methods=['POST'])
def upload_files():
    """处理文件上传并生成视频"""
    try:
        app.logger.info("接收到文件上传请求")
        
        # 获取片段数量
        segment_count = int(request.form.get('segment_count', 1))
        app.logger.info(f"片段数量: {segment_count}")
        
        if segment_count < 1:
            return jsonify({'error': '至少需要一个片段'}), 400
        
        # 创建临时目录用于存储处理文件
        temp_dir = os.path.join(UPLOAD_FOLDER, f"temp_{uuid.uuid4().hex}")
        os.makedirs(temp_dir, exist_ok=True)
        app.logger.info(f"创建临时目录: {temp_dir}")
        
        # 存储每个片段的文件路径
        segment_files = []
        segment_videos = []
        first_image_size = None
        
        # 处理每个片段
        for i in range(segment_count):
            # 检查是否有文件上传
            image_key = f'image-{i}'
            audio_key = f'audio-{i}'
            srt_key = f'srt-{i}'
            
            if image_key not in request.files or audio_key not in request.files or srt_key not in request.files:
                app.logger.error(f"片段 #{i+1} 缺少必要的文件")
                return jsonify({'error': f'片段 #{i+1} 缺少必要的文件'}), 400
            
            image_file = request.files[image_key]
            audio_file = request.files[audio_key]
            subtitle_file = request.files[srt_key]
            
            # 检查文件名是否为空
            if image_file.filename == '' or audio_file.filename == '' or subtitle_file.filename == '':
                app.logger.error(f"片段 #{i+1} 有空文件名")
                return jsonify({'error': f'片段 #{i+1} 有空文件名'}), 400
            
            # 为每个片段创建子目录
            segment_dir = os.path.join(temp_dir, f'segment_{i}')
            os.makedirs(segment_dir, exist_ok=True)
            
            # 保存上传的文件
            image_path = os.path.join(segment_dir, secure_filename(image_file.filename))
            audio_path = os.path.join(segment_dir, secure_filename(audio_file.filename))
            subtitle_path = os.path.join(segment_dir, secure_filename(subtitle_file.filename))
            
            image_file.save(image_path)
            audio_file.save(audio_path)
            subtitle_file.save(subtitle_path)
            
            app.logger.info(f"片段 #{i+1} 文件已保存: 图片={image_path}, 音频={audio_path}, 字幕={subtitle_path}")
            
            # 存储文件路径
            segment_files.append({
                'image': image_path,
                'audio': audio_path,
                'subtitle': subtitle_path,
                'index': i
            })
            
            # 如果是第一个图片，获取其尺寸作为标准
            if i == 0:
                with Image.open(image_path) as img:
                    first_image_size = img.size
                    app.logger.info(f"第一个图片尺寸: {first_image_size}")
        
        # 处理每个片段并生成单独的视频
        for segment in segment_files:
            # 如果不是第一个图片，调整尺寸以匹配第一个图片
            if segment['index'] > 0:
                with Image.open(segment['image']) as img:
                    if img.size != first_image_size:
                        app.logger.info(f"调整片段 #{segment['index']+1} 图片尺寸从 {img.size} 到 {first_image_size}")
                        # 保持长宽比缩放
                        img_resized = img.resize(first_image_size, Image.LANCZOS)
                        # 保存调整后的图片
                        resized_image_path = os.path.join(os.path.dirname(segment['image']), 'resized_' + os.path.basename(segment['image']))
                        img_resized.save(resized_image_path)
                        segment['image'] = resized_image_path
                        app.logger.info(f"调整后的图片已保存: {resized_image_path}")
            
            # 为每个片段创建输出视频路径
            segment_output = os.path.join(temp_dir, f'segment_{segment["index"]}_output.mp4')
            
            try:
                # 生成视频
                app.logger.info(f"开始生成片段 #{segment['index']+1} 的视频...")
                video = create_video_segment(segment['subtitle'], segment['audio'], segment['image'])
                
                # 写入视频文件
                app.logger.info(f"开始写入片段 #{segment['index']+1} 的视频文件: {segment_output}")
                # 确保视频有正确的fps设置
                if not hasattr(video, 'fps') or video.fps is None:
                    video.fps = 24
                
                # 使用更详细的参数设置
                video.write_videofile(
                    segment_output, 
                    codec='libx264', 
                    audio_codec='aac', 
                    fps=24,
                    threads=4,
                    preset='medium',
                    logger=None
                )
                app.logger.info(f"片段 #{segment['index']+1} 视频文件写入完成: {segment_output}")
                
                segment_videos.append(segment_output)
            
            except Exception as e:
                app.logger.error(f"生成片段 #{segment['index']+1} 视频时出错: {str(e)}")
                return jsonify({'error': f"生成片段 #{segment['index']+1} 视频时出错: {str(e)}"}), 500
        
        # 生成唯一的最终输出文件名
        timestamp = hashlib.md5(str(time.time()).encode()).hexdigest()[:8]
        output_filename = f"output_{timestamp}.mp4"
        output_path = os.path.join(OUTPUT_FOLDER, output_filename)
        
        # 确保输出目录存在
        os.makedirs(OUTPUT_FOLDER, exist_ok=True)
        
        # 如果只有一个片段，直接复制
        if len(segment_videos) == 1:
            app.logger.info(f"只有一个视频片段，直接复制到最终输出: {output_path}")
            import shutil
            shutil.copy(segment_videos[0], output_path)
        else:
            # 如果有多个片段，合并它们
            app.logger.info(f"合并 {len(segment_videos)} 个视频片段到最终输出: {output_path}")
            from moviepy.editor import VideoFileClip
            success = merge_videos(segment_videos, output_path)
            if not success:
                app.logger.error("合并视频失败")
                return jsonify({'error': '合并视频失败'}), 500
        
        # 返回视频URL
        video_url = url_for('serve_video', filename=output_filename, _external=True)
        app.logger.info(f"生成的视频URL: {video_url}")
        return jsonify({
            'success': True, 
            'url': video_url,
            'filename': output_filename
        })
    
    except Exception as e:
        app.logger.error(f"处理上传文件时出错: {str(e)}")
        import traceback
        app.logger.error(traceback.format_exc())
        return jsonify({'error': str(e)}), 500

@app.route('/outputs/<filename>')
def serve_video(filename):
    """提供生成的视频文件"""
    try:
        # 检查文件是否存在
        file_path = os.path.join(app.config['OUTPUT_FOLDER'], filename)
        if not os.path.exists(file_path):
            app.logger.error(f"视频文件不存在: {file_path}")
            return jsonify({'error': '视频文件不存在'}), 404
        
        # 记录文件大小
        file_size = os.path.getsize(file_path)
        app.logger.info(f"提供视频文件: {file_path}, 大小: {file_size} 字节")
        
        # 明确设置MIME类型
        response = send_from_directory(app.config['OUTPUT_FOLDER'], filename, mimetype='video/mp4')
        return response
    except Exception as e:
        app.logger.error(f"提供视频文件时出错: {str(e)}")
        return jsonify({'error': str(e)}), 500

@app.route('/test-srt', methods=['POST'])
def test_srt():
    """测试字幕文件解析"""
    try:
        if 'srt' not in request.files:
            return jsonify({'error': '未找到字幕文件'}), 400
        
        srt_file = request.files['srt']
        if srt_file.filename == '':
            return jsonify({'error': '未选择文件'}), 400
        
        # 保存文件
        os.makedirs(UPLOAD_FOLDER, exist_ok=True)
        srt_path = os.path.join(UPLOAD_FOLDER, secure_filename(srt_file.filename))
        srt_file.save(srt_path)
        
        # 解析字幕文件
        subs = pysrt.open(srt_path)
        
        # 返回解析结果
        subtitles = []
        for sub in subs:
            subtitles.append({
                'index': sub.index,
                'start': str(sub.start),
                'end': str(sub.end),
                'text': sub.text
            })
        
        return jsonify({
            'success': True,
            'subtitle_count': len(subtitles),
            'subtitles': subtitles
        })
    
    except Exception as e:
        app.logger.error(f"测试字幕文件时出错: {str(e)}")
        return jsonify({'error': str(e)}), 500

@app.route('/system-check')
def system_check():
    """检查系统状态"""
    try:
        result = {
            'status': 'ok',
            'checks': {
                'imagemagick': check_imagemagick(),
                'moviepy_config': check_moviepy_config(),
                'uploads': check_directory_writable(UPLOAD_FOLDER),
                'outputs': check_directory_writable(OUTPUT_FOLDER)
            }
        }
        
        # 确定整体状态
        if any(check.get('error') for check in result['checks'].values() if isinstance(check, dict)):
            result['status'] = 'error'
        elif any(not check.get('installed', True) or not check.get('exists', True) or not check.get('writable', True) 
                for check in result['checks'].values() if isinstance(check, dict)):
            result['status'] = 'warning'
            
        return jsonify(result)
    except Exception as e:
        app.logger.error(f"检查系统状态时出错: {str(e)}")
        return jsonify({'status': 'error', 'error': str(e)}), 500

def check_imagemagick():
    """检查ImageMagick是否已安装"""
    try:
        result = subprocess.run(['convert', '--version'], capture_output=True, text=True)
        if result.returncode == 0 and 'ImageMagick' in result.stdout:
            version = result.stdout.split('\n')[0]
            return {'installed': True, 'version': version}
        return {'installed': False, 'error': 'ImageMagick未正确安装'}
    except Exception as e:
        return {'installed': False, 'error': str(e)}

def check_moviepy_config():
    """检查MoviePy配置"""
    try:
        from moviepy.config import get_setting
        imagemagick_binary = get_setting("IMAGEMAGICK_BINARY")
        return {
            'exists': bool(imagemagick_binary),
            'imagemagick_binary': imagemagick_binary
        }
    except Exception as e:
        return {'exists': False, 'error': str(e)}

def check_directory_writable(directory):
    """检查目录是否可写"""
    try:
        os.makedirs(directory, exist_ok=True)
        test_file = os.path.join(directory, '.write_test')
        with open(test_file, 'w') as f:
            f.write('test')
        os.remove(test_file)
        return {'writable': True}
    except Exception as e:
        return {'writable': False, 'error': str(e)}

def merge_videos(video_paths, output_path):
    """
    合并多个视频片段为一个视频
    
    Args:
        video_paths: 视频文件路径列表
        output_path: 输出视频路径
        
    Returns:
        bool: 是否成功
    """
    try:
        app.logger.info(f"开始合并 {len(video_paths)} 个视频片段")
        
        # 加载所有视频剪辑
        video_clips = []
        for path in video_paths:
            app.logger.info(f"加载视频片段: {path}")
            # 明确从moviepy.editor导入VideoFileClip
            from moviepy.editor import VideoFileClip
            clip = VideoFileClip(path)
            video_clips.append(clip)
        
        # 合并视频
        app.logger.info("合并视频片段")
        final_clip = concatenate_videoclips(video_clips, method="compose")
        
        # 导出视频
        app.logger.info(f"导出合并后的视频: {output_path}")
        final_clip.write_videofile(
            output_path, 
            codec='libx264', 
            audio_codec='aac', 
            fps=24,
            threads=4,
            preset='medium',
            logger=None
        )
        
        # 关闭剪辑
        app.logger.info("关闭视频剪辑")
        final_clip.close()
        for clip in video_clips:
            clip.close()
            
        app.logger.info("视频合并完成")
        return True
    except Exception as e:
        app.logger.error(f"合并视频时出错: {str(e)}")
        import traceback
        app.logger.error(traceback.format_exc())
        return False

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)